#ifndef _core_switch_detail_value_at_impl_hpp_
#define _core_switch_detail_value_at_impl_hpp_

/** \file value_at_impl.hpp
 *  \author Adrian Schweizer
 *  \created  $Mo 11 Feb 08:33:01 pm CET 2008 schwadri@SchwadriLaptop.local$
 *  \modified $Mo 11 Feb 08:40:01 pm CET 2008 schwadri@SchwadriLaptop.local$
 */

#include <boost/mpl/void.hpp>
#include <boost/mpl/greater_equal.hpp>
#include <boost/mpl/minus.hpp>
#include <boost/mpl/size_t.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/fusion/sequence/intrinsic/size.hpp>
#include "case_invoker.hpp"

namespace core {
    struct default_label;
    struct case_tag;
}

namespace boost {
    namespace fusion {
        namespace extension {

            template <typename Tag>
                struct value_at_impl;

            template <>
                struct value_at_impl<core::case_tag>
                {
                    template <typename Case, typename Index, std::size_t Offset>
                        struct get_case_invoker;

                    struct check_label_sequence
                    {
                        template <typename Case, typename Index, std::size_t Offset>
                            struct apply
                            :   mpl::eval_if<
                                    mpl::greater_equal<Index,mpl::size<typename Case::labels> >,
                                    get_case_invoker<typename Case::next,mpl::minus<Index,mpl::size<typename Case::labels> >,Offset+1>,
                                    core::case_invoker<Case,typename mpl::at<typename Case::labels,Index>::type,mpl::size_t<Offset> >
                                >
                            { };
                    };

                    struct check_label
                    {
                        template <typename Case, typename Index, std::size_t Offset>
                            struct apply
                            :   mpl::eval_if<
                                    is_same<typename Case::labels,core::default_label>,
                                    //if default label we just ignore that case and jump to the next
                                    get_case_invoker<typename Case::next,Index,Offset+1>,
                                    mpl::eval_if<
                                        mpl::greater_equal<Index,mpl::size_t<1> >,
                                        get_case_invoker<typename Case::next,mpl::minus<Index,mpl::size_t<1> >,Offset+1>,
                                        core::case_invoker<Case,typename Case::labels,mpl::size_t<Offset> >
                                    >
                                >
                            { };
                    };

                    template <typename Case, typename Index, std::size_t Offset>
                        struct get_case_invoker
                        :   mpl::if_< mpl::is_sequence<typename Case::labels>,
                                        check_label_sequence,
                                        check_label
                                    >::type::template apply<Case,Index,Offset>
                        { };

                    template <typename Index, std::size_t Offset>
                        struct get_case_invoker<mpl::void_,Index,Offset>
                        :   mpl::void_
                        { };


                    template <typename Cases, typename Index>
                        struct apply
                        {
                            typedef typename get_case_invoker<Cases,Index,0>::type   type;
                        };
            };
        }
    }
}


#endif // _core_switch_detail_value_at_impl_hpp_
